using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;

namespace Mud.Common.Messages
{
    /// <summary>
    /// Finds and creates a message handler for specific message. The assembly that is calling this
	/// object will be used as the source of message handler types.
    /// </summary>
	/// <typeparam name="T">Base type for the message handlers. Different base types can be used.</typeparam>
    public class MessageHandlerFactory<T> where T: class
    {

		private Dictionary<int, Type> _handlerTypes = new Dictionary<int, Type>();


        /// <summary>
        /// Associates a message handler class with specific message type by using reflection.
        /// Assembly that initialized this factory will be used as a source of types.
        /// </summary>
        public MessageHandlerFactory()
            : this(new Assembly[] { Assembly.GetCallingAssembly() }) 
        {
        }

		/// <summary>
		/// Associates a message handler class with specific message type by using reflection.
		/// </summary>
        /// <param name="assembliesToSearch">List of assemblies that will be searched for
        /// message handlers.</param>
		public MessageHandlerFactory(Assembly[] assembliesToSearch)
        {
            foreach (Assembly assembly in assembliesToSearch)
            {
                Type[] allTypes = assembly.GetTypes();
                foreach (Type type in allTypes)
                {
                    if (!typeof(T).IsAssignableFrom(type)) // go through derived classes only
                        continue;
                    object[] attributes = type.GetCustomAttributes(typeof(MessageHandlerAttribute), false);
                    if (attributes == null || attributes.Length == 0)
                        continue;
                    foreach (MessageHandlerAttribute attribute in attributes)
                        _handlerTypes.Add((int)attribute.MessageTypeId, type);
                }
            }
		}

        /// <summary>
        /// Creates a message handler out of the message.
        /// </summary>
		public T CreateHandler(IMessage message)
        {
			Type messageHandlerType = null;
			if (!_handlerTypes.TryGetValue(message.MessageTypeId, out messageHandlerType)) {
                Logger.Log(LogType.Error, "MessageHandler", "There is no message handler to handle message of type {0}.", (MessageTypeIds)message.MessageTypeId);
				return null;
			}

			return (T)Activator.CreateInstance(messageHandlerType);
		}

    }
}
